#ifndef CODE_GENERATOR_H
#define CODE_GENERATOR_H

#include <iostream>
#include <fstream>
#include <vector>
#include <sstream>
#include <map>
#include <stdlib.h>
#include "exceptions.h"
#include "types.h"
#include "tree.h"
#include "token.h"

/* Генератор коду. Су-переклад дерева операцій на мову ассемблера */

/* Якщо на одному рівні знаходяться більше однієї операції, то вибирається та операція для заміни поточного вузла, яка спочатку
        розташовувалася вище інших операцій. Це правило використовується при побудові дерева операцій */

class CodeGenerator {
    std::vector<TreeOutput*> array_operations_trees;
    TreeOutput *operations_tree; // поточне дерево виводу. Використовується для заповнення mas_trees
    std::map<std::string, IdentificatorEntry> identificators_table; // таблиця ідентифікаторів кожної окремої ф-ції.
                                                                   // Містить ідентифікатори тієї ф-ції, код якої генерується
    std::map<std::string, std::map<std::string, IdentificatorEntry> > func_identificators_table; // таблиця ідентифікаторів ф-цій
    void __convertOutputTreeToOperationsTree(TreeOutput*, TreeOutput*, uint = 0, uint = 0); // перетворення дерева виводу в дерево операцій
    std::ofstream asm_code_file; // лістинг на мові ассемблера
    void __startCodeGenerate(const std::vector<TreeOutput*>&); // розпочинає генерацію коду
    void __sysStartCodeGenerate(TreeOutput*); // генерація коду на ассемблері
    void __placementLocalVariables(std::map<std::string, IdentificatorEntry>&); // розміщує локальні змінні в стеку та виконує пролог
    void __placementGlobalVariables(); // розміщує глобальні змінні в секції .data
    void __placementFunctionParam(std::map<std::string, IdentificatorEntry>&); // розміщує в стеку параметри ф-ції перед її визовом
    void __variablesOperationsGenerate(TreeOutput*); // генератор коду арифметичних операцій із змінними та масивами
    void __appropriationOperationCodeGenerator(TreeOutput*); // генератор коду операції присвоєння
    void __multiplicationOperationCodeGenerator(TreeOutput*); // генератор коду операції множення
    void __divisionOperationCodeGenerator(TreeOutput*); // генератор коду операції ділення
    void __relatedOperationsCodeGenerator(TreeOutput*); // генератор коду операцій відношення
    void __addSubOperationsCodeGenerator(TreeOutput*); // генератор коду операцій додавання та віднімання
    void __moduleDivisionOperationCodeGenerator(TreeOutput*); // генератор коду операції ділення по модулю
    void __loopsCodeGenerator(TreeOutput*); // генератор коду циклів
    void __conditionalCodeGenerator(TreeOutput*); // генератор коду умовного оператора
    void __callFunctionCodeGenerator(TreeOutput*); // генератор коду визову ф-цій
    void __callFunctionTextGenerate(TreeOutput*, uint); // генерує код визову ф-цій. Використовується __callFunctionCodeGenerator()
    // ф-ція повертає зміщення відносно регістра %ebp для n-го елемента масиву
    std::string __arrayElementAddressCodeGenerator(TreeOutput*, sint, bool = false);
    // ф-ція повертає слідуючий за element_pos правильний операнд (тому що масив має індекс)
    ushort __getNextOperand(TreeOutput*, ushort&);
    void __dualNodesAreOperands(TreeOutput*, std::ostringstream&, std::ostringstream&); // обидва дочірні вузли - операнди
    void __leftNodeIsOperation(TreeOutput*, std::ostringstream&); // тільки лівий вузол - операція
    void __rightNodeIsOperation(TreeOutput*, std::ostringstream&); // тільки правий вузол - операція
    std::string __getOperand(TreeOutput*, ushort&, bool = false); // повертає правильний операнд для масивів та не масивів
    ushort __initArrayCodeGenerator(TreeOutput*); // генератор коду ініціалізації масиву
    void __priorityToOperations(TreeOutput*, uint, bool = false); // забезпечує пріоритет виконання операцій
public:
    CodeGenerator();
     ~CodeGenerator()
    {
        for (uint i = 0; i < array_operations_trees.size(); i++) delete array_operations_trees[i];
    }
    void startCodeGenerate(const std::vector<TreeOutput*>&, std::map<std::string, std::map<std::string, IdentificatorEntry> >&);
    const std::vector<TreeOutput*>& getArrayOutputTrees() { return array_operations_trees; }
};

#endif // CODE_GENERATOR_H
